package paxos

import "fmt"

// Proposer 提议者
type Proposer struct {
	id        int   // 服务器id
	round     int   // 当前提议者已知最大轮次
	number    int   // 提案编号=(轮次,服务器id)
	acceptors []int // 接受者列表
}

// propose 提交提案（两阶段）
func (p *Proposer) propose(value interface{}) interface{} {
	p.round++
	p.number = p.proposalNumber()

	var prepareCount, maxNumber int
	// 第一阶段：prepare
	for _, aid := range p.acceptors {
		msgArgs := MsgArgs{
			Number: p.number,
			From:   p.id,
			To:     aid,
		}
		// 发送提案
		reply := new(MsgReply)
		ok := call(fmt.Sprintf("127.0.0.1:%d", aid), "Acceptor.Prepare", &msgArgs, reply)
		if !ok || !reply.Ok {
			continue
		}
		prepareCount++
		// 获得最大提案编号，并更新提案值
		if reply.Number > maxNumber {
			maxNumber = reply.Number
			value = reply.Value
		}
		// 如果多数接受提案，第一阶段就成功了，则跳出
		// 这里我一开始有个疑问，就是那些提案id是旧的较小值的接收者怎么办，这里直接break就不管了吗，
		// 如果你也有这个疑问，仔细看看接收者的accept函数和提案者第二阶段怎么处理的（没有break），你就会明白
		if prepareCount >= p.majority() {
			break
		}
	}
	if prepareCount < p.majority() {
		return nil
	}
	// 第二阶段：accept
	var acceptCount int
	for _, aid := range p.acceptors {
		msgArgs := MsgArgs{
			Number: p.number,
			Value:  value,
			From:   p.id,
			To:     aid,
		}
		reply := new(MsgReply)
		ok := call(fmt.Sprintf("127.0.0.1:%d", aid), "Acceptor.Accept", &msgArgs, reply)
		if !ok || !reply.Ok {
			continue
		}
		acceptCount++
	}
	if acceptCount >= p.majority() {
		return value
	}
	return nil
}

// majority 获得多数，为了计算是否成功
func (p *Proposer) majority() int {
	return len(p.acceptors)/2 + 1
}

// // 提案编号=(轮次,服务器id)
func (p *Proposer) proposalNumber() int {
	return p.round<<16 | p.id
}
